﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using YJKArch.Utils;

namespace YJKArch.DormantWindow
{
	public class DormantWindowCreator
	{
		private WallType MWallType
		{
			get
			{
				return this.m_WallTypeList[this.m_Form.WallTypeIndex];
			}
		}

		private RoofType MRoofType
		{
			get
			{
				return this.m_RoofTypeList[this.m_Form.RoofTypeIndex];
			}
		}

		private FamilySymbol MWindowType
		{
			get
			{
				return this.m_WindowTypeList[this.m_Form.WindowTypeIndex];
			}
		}

		private double MDormantWidth
		{
			get
			{
				return Common.MMToFeet(this.m_Form.DormantWidth);
			}
		}

		private double MMaxRidgeHeight
		{
			get
			{
				return Common.MMToFeet(this.m_Form.MaxRidgeHeight);
			}
		}

		private double MWallHeight
		{
			get
			{
				return Common.MMToFeet(this.m_Form.WallHeight);
			}
		}

		private double MEavesOffset
		{
			get
			{
				return Common.MMToFeet(this.m_Form.EavesOffset);
			}
		}

		private double MWindowBottomElevation
		{
			get
			{
				return Common.MMToFeet(this.m_Form.WindowBottomElevation);
			}
		}

		public DormantWindowCreator(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_doc = this.m_Revit.Application.ActiveUIDocument.Document;
		}

		public bool CreateDormantWindow(ref string message)
		{
			PlanarFace planarFace = null;
			Element host = null;
			if (!this.SelectPlanarFaceWidthRoof(ref planarFace, ref host))
			{
				return false;
			}
			double slopeAngle = planarFace.FaceNormal().AngleTo(XYZ.BasisZ);
			this.ShowDialog(slopeAngle);
			planarFace.FaceNormal();
			XYZ xyz = -new XYZ(planarFace.FaceNormal().X, planarFace.FaceNormal().Y, 0.0);
			this.m_DormantDirection = xyz.Normalize();
			XYZ ptPos = null;
			for (;;)
			{
				if (!this.PickLeftBottomPosition(slopeAngle, ref ptPos))
				{
					if (!this.m_Form.InfoChanged)
					{
						break;
					}
					this.m_Form.InfoChanged = false;
				}
				else
				{
					Transaction transaction = new Transaction(this.m_Revit.Application.ActiveUIDocument.Document);
					try
					{
						transaction.Start("create dormant window");
						if (!this.CreateDormantWindow(planarFace, host, ptPos))
						{
							MessageBox.Show("老虎窗超出屋顶范围，无法生成", "盈建科提示");
							transaction.RollBack();
						}
						else
						{
							transaction.Commit();
						}
					}
					catch (Exception)
					{
						transaction.RollBack();
					}
				}
			}
			this.m_Form.Close();
			this.JoinLinkedRoof(host);
			return true;
		}

		private void JoinLinkedRoof(Element host)
		{
			Transaction transaction = new Transaction(this.m_Revit.Application.ActiveUIDocument.Document);
			transaction.Start("join linked roof");
			foreach (Element element in this.m_NewFloorWithDormantWindows)
			{
				try
				{
					this.JoinLinkedElements(element, host);
				}
				catch (Exception)
				{
				}
			}
			transaction.Commit();
		}

		private bool CreateDormantWindow(PlanarFace roofFace, Element host, XYZ ptPos)
		{
			Line line = YJKLineEx.YJKGetUnBound(ptPos, XYZ.BasisZ);
			XYZ xyz = null;
			if (!this.PlanarFaceIntersectLine(roofFace, line, ref xyz))
			{
				return false;
			}
			XYZ item = this.XYZTo2d(xyz);
			double z = xyz.Z;
			ElementId levelId = null;
			double offset = 0.0;
			if (!this.GetLevel(z, ref levelId, ref offset))
			{
				return false;
			}
			XYZ xyz2 = Geometry.RotateTo(this.m_DormantDirection, -Math.PI*.5, XYZ.BasisZ);
			XYZ xyz3 = -xyz2;
			XYZ xyz4 = -this.m_DormantDirection;
			XYZ dormantDirection = this.m_DormantDirection;
			XYZ xyz5 = xyz + XYZ.BasisZ * this.MWallHeight;
			XYZ xyz6 = this.XYZTo2d(xyz5);
			Line line2 = YJKLineEx.YJKGetUnBound(xyz5, this.m_DormantDirection);
			XYZ xyz7 = null;
			if (!this.PlanarFaceIntersectLine(roofFace, line2, ref xyz7))
			{
				return false;
			}
			XYZ xyz8 = this.XYZTo2d(xyz7);
			XYZ xyz9 = xyz + xyz2 * this.MDormantWidth;
			XYZ item2 = this.XYZTo2d(xyz9);
			XYZ xyz10 = xyz9 + XYZ.BasisZ * this.MWallHeight;
			XYZ xyz11 = this.XYZTo2d(xyz10);
			Line line3 = YJKLineEx.YJKGetUnBound(xyz10, this.m_DormantDirection);
			XYZ xyz12 = null;
			if (!this.PlanarFaceIntersectLine(roofFace, line3, ref xyz12))
			{
				return false;
			}
			XYZ xyz13 = this.XYZTo2d(xyz12);
			XYZ xyz14 = Geometry.CalculatMidPoint(xyz, xyz9);
			this.XYZTo2d(xyz14);
			XYZ xyz15 = xyz14 + XYZ.BasisZ * this.MMaxRidgeHeight;
			this.XYZTo2d(xyz15);
			Line line4 = YJKLineEx.YJKGetUnBound(xyz15, this.m_DormantDirection);
			XYZ pt3d = null;
			if (!this.PlanarFaceIntersectLine(roofFace, line4, ref pt3d))
			{
				return false;
			}
			XYZ xyz16 = this.XYZTo2d(pt3d);
			List<XYZ> list = new List<XYZ>();
			list.Add(xyz);
			list.Add(xyz5);
			list.Add(xyz7);
			Wall wall = null;
			if (!this.CreateWall(roofFace, list, xyz3, levelId, offset, ref wall))
			{
				return false;
			}
			List<XYZ> list2 = new List<XYZ>();
			list2.Add(xyz9);
			list2.Add(xyz12);
			list2.Add(xyz10);
			Wall wall2 = null;
			if (!this.CreateWall(roofFace, list2, xyz2, levelId, offset, ref wall2))
			{
				return false;
			}
			List<XYZ> list3 = new List<XYZ>();
			list3.Add(xyz);
			list3.Add(xyz9);
			list3.Add(xyz10);
			list3.Add(xyz15);
			list3.Add(xyz5);
			Wall frontWall = null;
			if (!this.CreateWall(roofFace, list3, xyz4, levelId, offset, ref frontWall))
			{
				return false;
			}
			if (!this.CreateWindow(frontWall, xyz14 + XYZ.BasisZ * this.MWindowBottomElevation))
			{
				return false;
			}
			List<XYZ> list4 = new List<XYZ>();
			list4.Add(item);
			list4.Add(item2);
			list4.Add(xyz13);
			list4.Add(xyz16);
			list4.Add(xyz8);
			XYZ ptOut = Geometry.CalculatMidPoint(xyz8, xyz13);
			double angle = Geometry.getAngle(xyz8, ptOut);
			double angle2 = Geometry.getAngle(xyz8, xyz16);
			double a = Geometry.BetweenTheAngles(angle, angle2, true);
			double num = this.MEavesOffset * Math.Tan(a);
			double num2 = this.MRoofType.get_Parameter(BuiltInParameter.ROOF_ATTR_DEFAULT_THICKNESS_PARAM).AsDouble();
			XYZ xyz17 = roofFace.FaceNormal() * 100.0;
			double length = xyz17.GetLength();
			double a2 = Math.Acos(new XYZ(xyz17.X, xyz17.Y, 0.0).GetLength() / length);
			double num3 = num2 * Math.Tan(a2);
			xyz8 += xyz3 * this.MEavesOffset;
			xyz8 += xyz4 * num;
			xyz8 += dormantDirection * num3;
			xyz13 += xyz2 * this.MEavesOffset;
			xyz13 += xyz4 * num;
			xyz13 += dormantDirection * num3;
			xyz16 += dormantDirection * num3;
			xyz6 += xyz3 * this.MEavesOffset;
			xyz6 += xyz4 * this.MEavesOffset;
			xyz11 += xyz2 * this.MEavesOffset;
			xyz11 += xyz4 * this.MEavesOffset;
			double num4 = (this.MMaxRidgeHeight - this.MWallHeight) / (this.MDormantWidth / 2.0);
			List<RoofEdge> list5 = new List<RoofEdge>();
			RoofEdge item3 = new RoofEdge(YJKLineEx.YJKGetBound(xyz8, xyz6), true, num4);
			list5.Add(item3);
			RoofEdge item4 = new RoofEdge(YJKLineEx.YJKGetBound(xyz6, xyz11), false, 0.0);
			list5.Add(item4);
			RoofEdge item5 = new RoofEdge(YJKLineEx.YJKGetBound(xyz11, xyz13), true, num4);
			list5.Add(item5);
			RoofEdge item6 = new RoofEdge(YJKLineEx.YJKGetBound(xyz13, xyz16), false, 0.0);
			list5.Add(item6);
			RoofEdge item7 = new RoofEdge(YJKLineEx.YJKGetBound(xyz16, xyz8), false, 0.0);
			list5.Add(item7);
			double num5 = num4 * this.MEavesOffset;
			double num6 = z + this.MWallHeight;
			return this.GetLevel(num6 - num5, ref levelId, ref offset) && this.CreateRoof(list5, levelId, offset, host);
		}

		private XYZ XYZTo2d(XYZ pt3d)
		{
			return new XYZ(pt3d.X, pt3d.Y, 0.0);
		}

		private bool SelectPlanarFaceWidthRoof(ref PlanarFace roofFace, ref Element host)
		{
			Reference reference = null;
			Selection selection = this.m_Revit.Application.ActiveUIDocument.Selection;
			try
			{
				reference = selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Face, "选择布置老虎窗的屋顶平面");
			}
			catch (Exception)
			{
				return false;
			}
			if (reference == null)
			{
				return false;
			}
			Element element = this.m_doc.GetElement(reference);
			Face face = element.GetGeometryObjectFromReference(reference) as Face;
			if (face.GetType() != typeof(PlanarFace))
			{
				MessageBox.Show("不支持在曲面屋顶创建", "盈建科提示");
				return false;
			}
			roofFace = (face as PlanarFace);
			if (roofFace.FaceNormal().IsAlmostEqualTo(XYZ.BasisZ) || roofFace.FaceNormal().IsAlmostEqualTo(-XYZ.BasisZ))
			{
				MessageBox.Show("不支持在屋顶水平平面创建", "盈建科提示");
				return false;
			}
			if (Geometry.IsEqual(roofFace.FaceNormal().Z, 0.0))
			{
				MessageBox.Show("不支持在屋顶竖直平面创建", "盈建科提示");
				return false;
			}
			host = element;
			return true;
		}

		[DllImport("user32.dll", EntryPoint = "SendMessageA")]
		public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

		private void ShowDialog(double slopeAngle)
		{
			this.m_WallTypeList = this.GetWallType();
			this.m_RoofTypeList = this.GetRoofType();
			this.m_WindowTypeList = this.GetWindowType();
			WindowGeometryExtractor extractor = new WindowGeometryExtractor(this.m_Revit);
			this.m_Form = new SelectComponentsTypeForm(extractor, this.m_WallTypeList, this.m_RoofTypeList, this.m_WindowTypeList, slopeAngle);
			this.m_Form.Show(DormantWindowCreator._hWndRevit);
			int wMsg = 7;
			DormantWindowCreator.SendMessage(DormantWindowCreator._hWndRevit.Handle, wMsg, IntPtr.Zero, IntPtr.Zero);
		}

		private bool PickLeftBottomPosition(double slopeAngle, ref XYZ ptPos)
		{
			return DragJig.PickLeftBottomPosition(this.m_Revit, this.m_DormantDirection, this.MDormantWidth, this.MMaxRidgeHeight, this.MWallHeight, slopeAngle, ref ptPos);
		}

		private List<WallType> GetWallType()
		{
			List<WallType> list = new List<WallType>();
			foreach (Element element in new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document).OfClass(typeof(WallType)).ToElements())
			{
				WallType wallType = element as WallType;
				if (wallType.Kind == null)
				{
					list.Add(wallType);
				}
			}
			return list;
		}

		private List<RoofType> GetRoofType()
		{
			List<RoofType> list = new List<RoofType>();
			foreach (Element element in new FilteredElementCollector(this.m_Revit.Application.ActiveUIDocument.Document).OfClass(typeof(RoofType)).ToElements())
			{
				RoofType roofType = element as RoofType;
				CompoundStructure compoundStructure = roofType.GetCompoundStructure();
				if (compoundStructure != null)
				{
					list.Add(roofType);
				}
			}
			return list;
		}

		private List<FamilySymbol> GetWindowType()
		{
			List<FamilySymbol> list = new List<FamilySymbol>();
			foreach (Element element in new FilteredElementCollector(this.m_doc).WhereElementIsElementType().OfCategory(BuiltInCategory.OST_Windows).ToElements())
			{
				if (element.GetType() == typeof(FamilySymbol))
				{
					FamilySymbol familySymbol = element as FamilySymbol;
					try
					{
						if (familySymbol.Family.get_Parameter(BuiltInParameter.FAMILY_HOSTING_BEHAVIOR).AsInteger() != 1)
						{
							continue;
						}
					}
					catch (Exception)
					{
						continue;
					}
					list.Add(familySymbol);
				}
			}
			return list;
		}

		private bool CreateWall(PlanarFace roofFace, List<XYZ> loopPoints, XYZ normal, ElementId levelId, double offset, ref Wall wall)
		{
			IList<Curve> list = new List<Curve>();
			for (int i = 0; i < loopPoints.Count; i++)
			{
				XYZ startPoint = loopPoints[i];
				XYZ endPoint = loopPoints[0];
				if (i != loopPoints.Count - 1)
				{
					endPoint = loopPoints[i + 1];
				}
				Line item = YJKLineEx.YJKGetBound(startPoint, endPoint);
				list.Add(item);
			}
			wall = Wall.Create(this.m_doc, list, this.MWallType.Id, levelId, false, normal);
			if (wall == null)
			{
				return false;
			}
			wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(offset);
			return true;
		}

		private bool CreateWindow(Wall frontWall, XYZ position)
		{
			ElementId elementId = null;
			double num = 0.0;
			if (!this.GetLevel(position.Z, ref elementId, ref num))
			{
				return false;
			}
			Level level = this.m_doc.GetElement(elementId) as Level;
			UnifiedModified.ActivateFamilySymbol(this.MWindowType);
			FamilyInstance familyInstance = this.m_doc.Create.NewFamilyInstance(position, this.MWindowType, frontWall, level, 0);
			if (familyInstance == null)
			{
				return false;
			}
			familyInstance.get_Parameter(BuiltInParameter.INSTANCE_SILL_HEIGHT_PARAM).Set(num);
			return true;
		}

		private bool CreateHoleWithRoof(List<XYZ> holeLoop, Element host)
		{
			double elevation = this.m_Revit.Application.ActiveUIDocument.ActiveView.GenLevel.Elevation;
			CurveArray curveArray = this.m_Revit.Application.Application.Create.NewCurveArray();
			for (int i = 0; i < holeLoop.Count; i++)
			{
				XYZ xyz = holeLoop[i];
				XYZ xyz2 = holeLoop[0];
				if (i != holeLoop.Count - 1)
				{
					xyz2 = holeLoop[i + 1];
				}
				Line line = YJKLineEx.YJKGetBound(new XYZ(xyz.X, xyz.Y, elevation), new XYZ(xyz2.X, xyz2.Y, elevation));
				curveArray.Append(line);
			}
			Opening result = null;
			try
			{
				result = this.m_doc.Create.NewOpening(host, curveArray, false);
			}
			catch (Exception)
			{
				return true;
			}
			return result != null;
		}

		private bool CreateHoleWithRoof(List<XYZ> holeLoop, double bottomElevation, double topElevation)
		{
			ElementId elementId = null;
			double num = 0.0;
			if (!this.GetLevel(bottomElevation, ref elementId, ref num))
			{
				return false;
			}
			Level level = this.m_doc.GetElement(elementId) as Level;
			ElementId elementId2 = null;
			double num2 = 0.0;
			if (!this.GetLevel(topElevation, ref elementId2, ref num2))
			{
				return false;
			}
			Level level2 = this.m_doc.GetElement(elementId2) as Level;
			CurveArray curveArray = this.m_Revit.Application.Application.Create.NewCurveArray();
			for (int i = 0; i < holeLoop.Count; i++)
			{
				XYZ xyz = holeLoop[i];
				XYZ xyz2 = holeLoop[0];
				if (i != holeLoop.Count - 1)
				{
					xyz2 = holeLoop[i + 1];
				}
				Line line = YJKLineEx.YJKGetBound(new XYZ(xyz.X, xyz.Y, bottomElevation), new XYZ(xyz2.X, xyz2.Y, bottomElevation));
				curveArray.Append(line);
			}
			return this.m_doc.Create.NewOpening(level, level2, curveArray) != null;
		}

		private bool CreateRoof(List<RoofEdge> roofLoop, ElementId levelId, double offset, Element host)
		{
			CurveArray curveArray = this.m_Revit.Application.Application.Create.NewCurveArray();
			foreach (RoofEdge roofEdge in roofLoop)
			{
				curveArray.Append(roofEdge.Edge);
			}
			Level level = this.m_doc.GetElement(levelId) as Level;
			ModelCurveArray modelCurveArray = new ModelCurveArray();
			FootPrintRoof footPrintRoof = this.m_doc.Create.NewFootPrintRoof(curveArray, level, this.MRoofType, out modelCurveArray);
			if (footPrintRoof == null)
			{
				return false;
			}
			footPrintRoof.get_Parameter(BuiltInParameter.ROOF_LEVEL_OFFSET_PARAM).Set(offset);
			ModelCurveArrayIterator modelCurveArrayIterator = modelCurveArray.ForwardIterator();
			modelCurveArrayIterator.Reset();
			int num = 0;
			while (modelCurveArrayIterator.MoveNext())
			{
				RoofEdge roofEdge2 = roofLoop[num];
				ModelCurve modelCurve = modelCurveArrayIterator.Current as ModelCurve;
				footPrintRoof.set_DefinesSlope(modelCurve, roofEdge2.DefinesSlope);
				if (roofEdge2.DefinesSlope)
				{
					footPrintRoof.set_SlopeAngle(modelCurve, roofEdge2.SlopeAngle);
				}
				num++;
			}
			this.m_NewFloorWithDormantWindows.Add(footPrintRoof);
			return true;
		}

		private void JoinLinkedElements(Element element1, Element element2)
		{
			try
			{
				JoinGeometryUtils.JoinGeometry(this.m_doc, element1, element2);
			}
			catch (Exception ex)
			{
				string message = ex.Message;
			}
		}

		private bool PlanarFaceIntersectLine(PlanarFace face, Line line, ref XYZ ptInter)
		{
			List<XYZ> list = new List<XYZ>();
			IntersectionResultArray intersectionResultArray;
			if (face.Intersect(line, out intersectionResultArray) == SetComparisonResult.Overlap)
			{
				foreach (object obj in intersectionResultArray)
				{
					IntersectionResult intersectionResult = (IntersectionResult)obj;
					list.Add(intersectionResult.XYZPoint);
				}
			}
			if (list.Count != 1)
			{
				return false;
			}
			ptInter = list[0];
			return true;
		}

		private bool GetLevel(double elevation, ref ElementId levelId, ref double offset)
		{
			List<Level> sortLevels = Common.GetSortLevels(this.m_doc, double.MinValue, double.MaxValue);
			double num = double.MaxValue;
			int i = 0;
			int num2 = -1;
			while (i < sortLevels.Count)
			{
				Level level = sortLevels[i];
				double num3 = elevation - level.Elevation;
				if (Geometry.LessThan(Math.Abs(num3), Math.Abs(num)))
				{
					num2 = i;
					num = num3;
				}
				i++;
			}
			if (num2 != -1)
			{
				levelId = sortLevels[num2].Id;
				offset = num;
				return true;
			}
			MessageBox.Show("无法匹配到当前的标高，请先添加相应的标高", "盈建科提示");
			return false;
		}

		public static WindowHandle _hWndRevit;

		private ExternalCommandData m_Revit;

		private Autodesk.Revit.DB.Document m_doc;

		private XYZ m_DormantDirection;

		private List<WallType> m_WallTypeList;

		private List<RoofType> m_RoofTypeList;

		private List<FamilySymbol> m_WindowTypeList;

		private SelectComponentsTypeForm m_Form;

		private List<Element> m_NewFloorWithDormantWindows = new List<Element>();
	}
}
